#!/bin/bash
# Functions for use by VMM scripts
# Author: Henrique Teófilo <henriquetft@gmail.com>

# config environment
BASE_DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))
source ${BASE_DIR}/vboxrc

CMD_MANAGE=${CMD_VBOX_MANAGE:-VBoxManage}

DEBUG='no'


####################### [ UTILITY FUNCTIONS (PRIVATE) ] ########################


function debug() {
	[ "$DEBUG" = 'yes' ] && {
		echo "DEBUG:" $*
	}

	[ "$ONE_VBOX_LOG" ] && [ -w "$(dirname "$ONE_VBOX_LOG")" ] && {
		echo $* >> "$ONE_VBOX_LOG"
	}
}


function probe_medium_type() {
	# find out medium type for each UUID (needed for closemedium)
	# $1 = uuid
	if [ "$("$CMD_MANAGE" list dvds | grep $1)" ]; then
		echo "dvd";
	elif [ "$("$CMD_MANAGE" list hdds | grep $1)" ]; then
		echo "disk"
	fi
	# NOTE: add floppy ?
}


function uuids_for_controller() {
	# $1 = vm name
	# $2 = controller name
	echo $("$CMD_MANAGE" showvminfo "$1" | grep -i "$2" | grep UUID | \
                                       cut -f3 -d':' | tr -d ') ')
}


############################## [ VBOX FUNCTIONS ] ##############################


function vbox_destroy_vm() {
	[ "$1" ] || return 1
	local NAME="$1"
	if [ -z "$("$CMD_MANAGE" list vms | grep "\"${NAME}\"")" ]; then
		return 2
	fi

	# get all storage controllers (replaces space for '|' in its names)
	local CONTROLLERS=$("$CMD_MANAGE" showvminfo "$NAME" | \
	                    grep 'Storage Controller Name' | cut -f2 -d':' | \
	                    sed 's/^[[:space:]]*\(.*\)[[:space:]]*$/\1/' | \
	                    tr ' ' '|')

	# remove each controller and its respectives hdds or dvds
	for x in $CONTROLLERS; do
		local C=$(echo $x | tr '|' ' ')
		local uuids=$(uuids_for_controller "$NAME" "$C")
		"$CMD_MANAGE" storagectl "$NAME" --name "$C" --remove

		#remove_medium_for_controller "$NAME" "$C"
		# closing all images
		for y in $uuids; do
			local MEDIUM_TYPE=$(probe_medium_type $y)
			# returns error if the image is attached to another vm, but it's ok
			"$CMD_MANAGE" closemedium "$MEDIUM_TYPE" "$y" &> /dev/null
		done
	done

	"$CMD_MANAGE" unregistervm "${NAME}" --delete
	# also check closemedium errors
	return $?
}


# returns the state of vm (blank spaces are replaced by '_')
function vbox_get_vm_state() {
	local STATE='<not-found>'
	local RESULT=$("$CMD_MANAGE" showvminfo "$1" | grep 'State: ')
	if [ $? = 0 -a "$RESULT" ]; then
		STATE=$(echo $RESULT | cut -b8- | cut -d'(' -f1)
		STATE=$(echo $STATE | tr ' ' '_') 
	fi
	echo "$STATE"
}



function vbox_shutdown() {
	# $1 = name of vm
	# $2 = acpi (optional to try acpi first)
	# $3 = number of checkings if vm is powered off (optional along with acpi)
	# $4 = interval between checkings (optional along with acpi)

	[ "$1" ] || return 1
	local DEPLOY_ID=$1
	local TRY_ACPI='no'
	if [ "$2" = 'acpi' ]; then
		local TRY_ACPI='yes'
		local NUMBER=${3:-5}
		local INTERVAL=${4:-4}
		debug "It will try ACPI first... $NUMBER $INTERVAL"
	fi

	local CURRENT_STATE="$(vbox_get_vm_state $DEPLOY_ID)"
	if [ "$CURRENT_STATE" = 'powered_off' -o "$CURRENT_STATE" = '<not-found>' ]
	then
		debug "VM is already powered off or does not exist"
		return 0
	fi

	# turn to 'no' if shutdown is done by acpi
	local FORCE_SHUTDOWN='yes'
	local ACPI=$("$CMD_MANAGE" showvminfo "$DEPLOY_ID" | grep '^ACPI:' | \
	                                                  cut -d':' -f2)
	# trying to do with ACPI power button
	if [ $TRY_ACPI = 'yes' -a $ACPI == 'on' ]; then
		debug "Trying with ACPI"
		"$CMD_MANAGE" controlvm "$DEPLOY_ID" acpipowerbutton
		local I=0
		while [ $I -lt $NUMBER ]; do
			sleep $INTERVAL
			CURRENT_STATE=$(vbox_get_vm_state $DEPLOY_ID)
			debug "VM current state:" $CURRENT_STATE
			if [ "$CURRENT_STATE" = 'powered_off' ]; then
				FORCE_SHUTDOWN='no'
				debug "vm powered off successfully using acpi"
				break
			fi
		I=$((I+1))
		done
	else
		debug "ACPI not supported or not requested"
		FORCE_SHUTDOWN='yes'
	fi

	# could not shutdown via ACPI
	if [ "$FORCE_SHUTDOWN" = 'yes' ]; then
		debug "Shutting down vm by force"
		"$CMD_MANAGE" controlvm "$DEPLOY_ID" poweroff &> /dev/null
	fi

	return 0
}


function vbox_run_vm() {
	debug "calling vbox_run_vm function"
	[ "$1" ] || { debug "missing argument"; return 1; }
	BASE_DIR=$(dirname $(readlink -f "${BASH_SOURCE[0]}"))

	SCRIPT_DEPLOY="${BASE_DIR}/deploy.py"
	if [ ! -f "$SCRIPT_DEPLOY"  ]; then
		debug "ERROR: could not find $SCRIPT_DEPLOY"
		return 2
	fi
	debug "running ${SCRIPT_DEPLOY} to deploy vm $1"
	VBOX_NAME="$(python "${SCRIPT_DEPLOY}" "$1")"
	local STATUS=$?
	echo "$VBOX_NAME"
	if [ $STATUS != 0 ]; then
		debug "error executing deploy script: $VBOX_NAME"
		return 3
	fi
	debug "VM deployed with name $VBOX_NAME"
	return 0
}
